package com.hmkj.core.service.simulation.impl;

import com.hmkj.common.baseMapper.GenericMapper;
import com.hmkj.common.utils.DateUtils;
import com.hmkj.common.utils.OrderNoUtils;
import com.hmkj.common.utils.PriceHelper;
import com.hmkj.common.utils.StringUtils;
import com.hmkj.core.eolinker.models.ModelRealTime;
import com.hmkj.core.exception.BussinessException;
import com.hmkj.core.mapper.account.AccountLogSimulationMapper;
import com.hmkj.core.mapper.account.AccountSimulationMapper;
import com.hmkj.core.mapper.simulation.OrderSimulationMapper;
import com.hmkj.core.model.BuyModel;
import com.hmkj.core.po.account.AccountLogSimulation;
import com.hmkj.core.po.account.AccountSimulation;
import com.hmkj.core.po.order.Order;
import com.hmkj.core.po.simulation.OrderSimulation;
import com.hmkj.core.po.user.User;
import com.hmkj.core.service.order.OrderOperationService;
import com.hmkj.core.service.simulation.OrderSimulationService;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * 模拟订单业务相关Service接口实现<br>
 *
 * @author eproo
 * @ClassName: OrderSimulationServiceImpl
 * @date 2018-07-09 02:35:10
 */
@Service
@Scope("prototype")
@Transactional(rollbackFor = Exception.class)
public class OrderSimulationServiceImpl implements OrderSimulationService {

    @Resource
    private OrderSimulationMapper orderSimulationMapper;
    @Resource
    private AccountSimulationMapper accountSimulationMapper;
    @Resource
    private AccountLogSimulationMapper accountLogSimulationMapper;
    @Resource
    private OrderOperationService orderOperationService;

    @Override
    public GenericMapper<OrderSimulation, Integer> _getMapper() {
        return orderSimulationMapper;
    }

    @Override
    /**
     * 统计模拟用户总盈亏 已結算的單子
     * @param userId 用户ID
     * @return 总盈亏
     */
    public BigDecimal getProfitCountByUser(int userId) {
        return orderSimulationMapper.sumProfit(userId, OrderSimulation.STATUS.T7.code);
    }

    /**
     * 模拟点买操作
     *
     * @param user          点买用户
     * @param total         总金额
     * @param modelRealTime 时间
     * @param use           use
     * @param model         model
     * @param totalNumber   总数量
     */
    public synchronized void buyOrderForSimulation(User user, BigDecimal total, ModelRealTime modelRealTime, double use, BuyModel model, double totalNumber) throws Exception {
        //保存订单信息
        String orderNo = OrderNoUtils.getSerialNumber();
        orderSimulationMapper.insert(new OrderSimulation(t -> {
            t.setUserId(user.getId());
            t.setOrderNo(orderNo);
            t.setType(OrderSimulation.TYPE.T2.code);
            t.setEquityCode(modelRealTime.getStock_code());
            t.setEquityName(modelRealTime.getCode_name());
            t.setDivideScale(model.getDivideScale());
            t.setUseRatio(BigDecimal.valueOf(use));
            t.setDeposit(model.getDeposit());
            // TODO: 2018-07-27
            t.setPaidFee(model.getPaidFee());
            t.setBuyPrice(new BigDecimal(modelRealTime.getLast_price()));
            t.setBuyDealPrice(new BigDecimal(modelRealTime.getLast_price()));
            t.setBuyDealTime(DateUtils.getNow());
            t.setBuyDealNumber((int) totalNumber);
            t.setPaidDiyan(BigDecimal.ZERO);
            t.setUnpaidDiyan(BigDecimal.ZERO);
            t.setBuyNumber((int) totalNumber);
            t.setTradeCredit(model.getTradeCredit());
            t.setBuyTime(DateUtils.getNow());
            t.setLossLimit(model.getLossLimit());
            t.setStatus(OrderSimulation.STATUS.T3.code);
            t.setDiyanTime(0);
            if (StringUtils.isNotBlank(user.getInviteId())) {
                t.setInviterId(user.getInviteId());
            }
        }));
        OrderSimulation orderSimulation = orderSimulationMapper.selectByOrderNo(orderNo);

        //冻结履约保证金， 扣除综合费
        AccountSimulation accountSimulation = accountSimulationMapper.selectOne(new AccountSimulation(t -> {
            t.setUserId(user.getId());
        }));

        //记录资金记录
        //冻结履约保证金
        AccountLogSimulation accountLogSimulation1 = AccountLogSimulation.buildLog(user.getId(), orderSimulation.getId(), model.getDeposit(), AccountLogSimulation.TYPE.T10, accountSimulation, "模拟交易冻结履约保证金" + model.getDeposit() + "元");
        accountLogSimulationMapper.insert(accountLogSimulation1);

        //扣除综合管理费
        AccountLogSimulation accountLogSimulation2 = AccountLogSimulation.buildLog(user.getId(), orderSimulation.getId(), model.getPaidFee(), AccountLogSimulation.TYPE.T11, accountSimulation, "扣除综合管理费" + model.getPaidFee() + "元");
        accountLogSimulationMapper.insert(accountLogSimulation2);

        //更新资金(-综合管理费，-履约保证金-综合管理费，+履约保证金)
        int i = accountSimulationMapper.updateAccountSimulate(model.getPaidFee().negate(), model.getDeposit().add(model.getPaidFee()).negate(), model.getDeposit(), BigDecimal.ZERO, model.getPaidFee(), BigDecimal.ZERO, user.getId());
        if (i != 1) {
            throw new BussinessException("更新模拟账户资金失败");
        }
    }

    @Override
    public void buyOrderSimulation(User user, ModelRealTime modelRealTime, BuyModel model) {
        //保存订单信息
        String orderNo = OrderNoUtils.getSerialNumber();
        orderSimulationMapper.insert(new OrderSimulation(t -> {
            t.setUserId(user.getId());
            t.setOrderNo(orderNo);
            t.setType(Order.TYPE.T1.code);
            t.setEquityCode(modelRealTime.getStock_code());
            t.setEquityName(modelRealTime.getCode_name());
            t.setDivideScale(model.getDivideScale());
            t.setUseRatio(model.getUseRatio());
            t.setDeposit(model.getDeposit());
            t.setProfitStopPrice(model.getProfitStopPrice());
            t.setLoseStopPrice(model.getLoseStopPrice());
            t.setPaidFee(model.getPaidFee());
            t.setBuyPrice(new BigDecimal(modelRealTime.getLast_price()));
            t.setBuyDealPrice(new BigDecimal(modelRealTime.getLast_price()));
            //建仓收取的管理费
            // t.setPaidFeeBuy(model.getPaidFee());
            t.setPaidDiyan(BigDecimal.ZERO);
            t.setUnpaidDiyan(BigDecimal.ZERO);
            t.setBuyNumber(model.getBuyNumber());
            t.setBuyDealNumber(model.getBuyNumber());
            t.setTradeCredit(model.getTradeCredit());
            t.setStopLossLine(model.getLoseStopLine());
            t.setStopProfitLine(model.getProfitStopLine());
            t.setBuyTime(DateUtils.getNow());
            t.setLossLimit(model.getLossLimit());
            t.setLossRate(model.getLossRate());
            t.setStatus(Order.STATUS.T3.code);
            t.setDiyanTime(0);
            if (StringUtils.isNotBlank(user.getInviteId())) {
                t.setInviterId(user.getInviteId());
            }
        }));
        OrderSimulation orderSimulation = orderSimulationMapper.selectByOrderNo(orderNo);
        String operation = "发起点买，客户ID：【 " + user.getId() + " 】, " + "股票代码 ：【 " + modelRealTime.getStock_code() + " 】, " + "股票名称：【 " + modelRealTime.getCode_name() + " 】, " + "点买额度 ：【 " + model.getTradeCredit() + " 】 万元, " + "可买股数 ：【 " + model.getBuyNumber() + " 】 股, ";
        // orderOperationService.saveOrderOperation(orderSimulation, operation);

        //冻结履约保证金， 扣除综合费
        AccountSimulation accountSimulation = accountSimulationMapper.selectByUID(user.getId());

        //记录资金记录
        //冻结履约保证金
        AccountLogSimulation accountLogSimulation = AccountLogSimulation.buildLog(user.getId(), orderSimulation.getId(), model.getDeposit(), AccountLogSimulation.TYPE.T10, accountSimulation, "点买交易冻结履约保证金" + model.getDeposit() + "元");
        accountLogSimulationMapper.insert(accountLogSimulation);

        //扣除综合管理费
        AccountLogSimulation accountLogSimulation1 = AccountLogSimulation.buildLog(user.getId(), orderSimulation.getId(), model.getPaidFee(), AccountLogSimulation.TYPE.T11, accountSimulation, "点买交易扣除综合管理费" + model.getPaidFee() + "元");
        accountLogSimulationMapper.insert(accountLogSimulation1);

        //更新资金(-综合管理费，-履约保证金-综合管理费，+履约保证金)
        int i = accountSimulationMapper.updateAccountSimulate(model.getPaidFee().negate(), model.getDeposit().add(model.getPaidFee()).negate(), model.getDeposit(), BigDecimal.ZERO, model.getPaidFee(), BigDecimal.ZERO, user.getId());
        if (i != 1) {
            throw new BussinessException("更新账户资金失败");
        }
    }

    @Override
    public void doOrderSimulateSell(OrderSimulation order, String last_price) {
        //盈利分成比例
        BigDecimal divideScale = order.getDivideScale();
        //履约保证金
        BigDecimal deposit = order.getDeposit();
        //止盈保证金
//        BigDecimal depositProfit = o.getDepositProfit();
        //计算盈亏
        //买入成交价
        BigDecimal buyDealprice = order.getBuyPrice();
        //买入成交数
        BigDecimal buyDealNum = BigDecimal.valueOf(order.getBuyDealNumber());
        //卖出成交价
        BigDecimal sellDealPrice = new BigDecimal(last_price);
        //卖出成交数
        Integer sellDealNum = order.getBuyDealNumber();
        //买入总金额
        BigDecimal buyTotal = buyDealprice.multiply(buyDealNum);
        //卖出总金额
        BigDecimal sellTotal = sellDealPrice.multiply(BigDecimal.valueOf(sellDealNum));
        //计算卖出总金额和买入总金额的差价
        BigDecimal account = sellTotal.subtract(buyTotal);
        //更新orderSimulation表信息的
        orderSimulationMapper.updateByPrimaryKeySelective(new OrderSimulation(t -> {
            t.setId(order.getId());
            t.setSellTime(DateUtils.getNow());
            t.setSellPrice(new BigDecimal(last_price));
            t.setSellDealPrice(new BigDecimal(last_price));
            t.setSellDealNumber(order.getBuyDealNumber());
            t.setSellDealTime(DateUtils.getNow());
            t.setStatus(OrderSimulation.STATUS.T7.code);
            t.setSettleTime(DateUtils.getNow());
            t.setProfitCount(account);
        }));

        //冻结履约保证金， 扣除综合费
        AccountSimulation accountSimulation = accountSimulationMapper.selectOne(new AccountSimulation(t -> {
            t.setUserId(order.getUserId());
        }));
        //盈利
        if (PriceHelper.isGreaterThan(account, BigDecimal.ZERO)) {

            //点买人盈利资金
            BigDecimal buyProfit = account.multiply(divideScale);

            //记录资金记录
            // 解冻履约保证金
            AccountLogSimulation accountLogSimulation1 = AccountLogSimulation.buildLog(order.getUserId(), order.getId(), deposit, AccountLogSimulation.TYPE.T14, accountSimulation, "模拟平仓解冻履约保证金" + deposit + "元");
            accountLogSimulationMapper.insert(accountLogSimulation1);

            //盈利
            AccountLogSimulation accountLogSimulation2 = AccountLogSimulation.buildLog(order.getUserId(), order.getId(), buyProfit, AccountLogSimulation.TYPE.T15, accountSimulation, "模拟平仓盈利" + buyProfit + "元");
            accountLogSimulationMapper.insert(accountLogSimulation2);

            //更新资金(+盈利，+履约保证金+盈利，-履约保证金)
            accountSimulationMapper.updateAccountSimulate(buyProfit, deposit.add(buyProfit), deposit.negate(), order.getTradeCredit(), BigDecimal.ZERO, buyProfit, order.getUserId());

        } else {
            //计算亏损
            if (PriceHelper.isGreaterThan(deposit, account.negate())) {
                //亏损少于履约保证金

                //记录资金记录
                // 解冻履约保证金
                AccountLogSimulation accountLogSimulation1 = AccountLogSimulation.buildLog(order.getUserId(), order.getId(), deposit, AccountLogSimulation.TYPE.T14, accountSimulation, "模拟平仓解冻履约保证金" + deposit + "元");
                accountLogSimulationMapper.insert(accountLogSimulation1);

                //亏损
                AccountLogSimulation accountLogSimulation2 = AccountLogSimulation.buildLog(order.getUserId(), order.getId(), account.negate(), AccountLogSimulation.TYPE.T16, accountSimulation, "模拟平仓亏损" + account.negate() + "元");
                accountLogSimulationMapper.insert(accountLogSimulation2);

                //更新资金(-亏损，+履约保证金-亏损，-履约保证金)
                accountSimulationMapper.updateAccountSimulate(account, deposit.add(account), deposit.negate(), order.getTradeCredit(), BigDecimal.ZERO, account, order.getUserId());
            } else {

                //记录资金记录
                // 解冻履约保证金
                AccountLogSimulation accountLogSimulation1 = AccountLogSimulation.buildLog(order.getUserId(), order.getId(), deposit, AccountLogSimulation.TYPE.T14, accountSimulation, "模拟平仓解冻履约保证金" + deposit + "元");
                accountLogSimulationMapper.insert(accountLogSimulation1);

                //亏损(穿仓)
                AccountLogSimulation accountLogSimulation2 = AccountLogSimulation.buildLog(order.getUserId(), order.getId(), deposit, AccountLogSimulation.TYPE.T16, accountSimulation, "模拟平仓亏损(穿仓)" + deposit + "元");
                accountLogSimulationMapper.insert(accountLogSimulation2);

                //更新资金(-履约保证金，+0，-履约保证金)
                accountSimulationMapper.updateAccountSimulate(deposit.negate(), BigDecimal.ZERO, deposit, order.getTradeCredit(), BigDecimal.ZERO, deposit.negate(), order.getUserId());

            }
        }
    }

    @Override
    public List<OrderSimulation> selectOrderSimulateForTime(Map<String, Object> param) {
        return orderSimulationMapper.findOrderSimulateForTime(param);
    }

    @Override
    public List<OrderSimulation> selectProfitOrderSimulateForTime(Map<String, Object> param) {
        return orderSimulationMapper.findProfitOrderSimulateForTime(param);
    }

    @Override
    public List<OrderSimulation> selectSimulationRank(String today) {
        return orderSimulationMapper.selectSimulationRank(today);
    }

    @Override
    public void doAlterStopLoss(OrderSimulation orderSimulation, BigDecimal stopLosePrice, BigDecimal needDeposit, BigDecimal lastPrice) throws Exception {
        if (stopLosePrice.compareTo(orderSimulation.getLoseStopPrice()) != 0) {
            //计算最大止损价格 = 最新价 - 履约保证金*最大止损保证金比例/买入数量
            BigDecimal maxStopLosePrice = lastPrice.subtract(orderSimulation.getDeposit().multiply(orderSimulation.getLossRate()).divide(new BigDecimal(orderSimulation.getBuyDealNumber()), 2, BigDecimal.ROUND_UP));
            //Rule:1.传入止损价大于等于最大止损价格 则直接修改止损价格
            //     2.传入止损价小于最大止损价格 则修改止损价格，并要追加保证金
            BigDecimal subValue = BigDecimal.ZERO; //价格的差值（根据原订单止损价 和 已计算的最大止损价比 取最低值的差值）
            if (orderSimulation.getLoseStopPrice().compareTo(maxStopLosePrice) == -1) {
                if (stopLosePrice.compareTo(orderSimulation.getLoseStopPrice()) == -1) {
                    subValue = orderSimulation.getLoseStopPrice().subtract(stopLosePrice);
                }
            } else {
                if (stopLosePrice.compareTo(maxStopLosePrice) == -1) {
                    subValue = maxStopLosePrice.subtract(stopLosePrice);
                }
            }
            if (subValue.compareTo(BigDecimal.ZERO) == 1) {
                //计算需要追加的保证金 = 止损差价 * 成交数量
                BigDecimal confNeedDeposit = subValue.multiply(new BigDecimal(orderSimulation.getBuyDealNumber()));
                if (needDeposit.compareTo(confNeedDeposit) != 0) {
                    throw new BussinessException("请刷新页面重新操作");
                }
                AccountSimulation accountSimulation = accountSimulationMapper.selectByUID(orderSimulation.getUserId());
                //资金记录
                AccountLogSimulation accountLogSimulation = AccountLogSimulation.buildLog(orderSimulation.getUserId(), orderSimulation.getId(), needDeposit, AccountLogSimulation.TYPE.T20, accountSimulation, "修改止损追加保证金：" + needDeposit + "元");
                if (accountLogSimulationMapper.insert(accountLogSimulation) != 1) {
                    throw new BussinessException("修改止损追加保证金资金记录失败");
                }
                //更新账户资金
                if (accountSimulationMapper.updateAccountSimulate(BigDecimal.ZERO, needDeposit.negate(), needDeposit, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, orderSimulation.getUserId()) != 1) {
                    throw new BussinessException("修改止损追加保证金失败");
                }
            }
            BigDecimal deposit = orderSimulation.getDeposit().add(needDeposit);
            orderSimulationMapper.updateByPrimaryKeySelective(new OrderSimulation(t -> {
                t.setId(orderSimulation.getId());
                t.setDeposit(deposit);
                t.setLoseStopPrice(stopLosePrice);
            }));
        }
    }
}
